﻿using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Inaction.Extensions.Business
{
    public interface ICode
    {
        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        string FileName { get; }
        string ReferencedAssemblies { get; }
        string BusinessClass { get; }
        string ConfigCode { get; }
        DateTime? UpdateTime { get; }
    }
    public static class ICodeExtensions
    {
        public static System.Reflection.Assembly CodeCompile(this ICode code)
        {
            var provider = CodeDomProvider.CreateProvider("CSharp");
            CompilerParameters cp = new CompilerParameters();

            foreach (var reference in code.ReferencedAssemblies.Split(';'))
            {
                if (string.IsNullOrEmpty(reference)) continue;
                var fileFullPath = GetFileFullPath(reference);
                cp.ReferencedAssemblies.Add(fileFullPath);
            }

            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;
            cp.CompilerOptions = "/optimize";

            CompilerResults cr = provider.CompileAssemblyFromSource(cp, code.ConfigCode);
            if (cr.Errors.HasErrors)
            {
                StringBuilder errorMsg = new StringBuilder();
                foreach (CompilerError err in cr.Errors)
                {
                    errorMsg.AppendLine("行" + err.Line + ",列" + err.Column + ":" + err.ErrorText);
                }
                System.Diagnostics.Debug.WriteLine(errorMsg.ToString());
                throw new System.Exception("编译错误:\r\n" + errorMsg.ToString());
            }
            else
            {
                return cr.CompiledAssembly;
            }
        }

        private static string GetFileFullPath(string a)
        {
            string file = string.Empty;
            if (System.IO.File.Exists(a))
            {
                file = a;
            }
            else if (System.IO.File.Exists(a + ".dll"))
            {
                file = a + ".dll";
            }
            else if (System.IO.File.Exists(a + ".exe"))
            {
                file = a + ".exe";
            }
            else
            {
                file = a;
                if (!file.EndsWith(".dll")) file = a + ".dll";
                file = System.IO.Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.Windows)
                    + @"\Microsoft.NET\Framework" + (Environment.Is64BitProcess ? "64\\v" : "\\v")
                    + Environment.Version.Major + "." + Environment.Version.Minor + "." + Environment.Version.Build, file);
            }
            return file;
        }

        public static T GetInstance<T>(this ICode code, System.Reflection.Assembly assembly = null)
        {
            if (assembly == null)
            {
                assembly = CodeCompile(code);
            }
            var type = assembly.GetTypes()[0];
            var instance = type.GetConstructor(System.Type.EmptyTypes).Invoke(null);
            var logic = (T)instance;
            return logic;
        }
    }
}
